{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Impedance Matching"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "The general problem is illustrated by the figure below: a generator with an internal impedance $Z_S$ delivers a power to a passive load $Z_L$, through a 2-ports matching network. This problem is commonly named as \"the double matching problem\". Impedance matching is important for the following reasons:\n",
    "\n",
    " - maximizing the power transfer. Maximum power is delivered to the load when the generator _and_ the load are matched to the line and power loss in the line minimized\n",
    " - improving signal-to-noise ratio of the system\n",
    " - reducing amplitude and phase errors\n",
    " - reducing reflected power toward generator\n",
    " \n",
    "<img src=\"figures/Impedance_matching_general.svg\">\n",
    "\n",
    "As long as the load impedance $Z_L$ has a real positive part, a matching network can always be found. Many choices are available and the examples below only describe a few. The examples are taken from the D.Pozar book \"Microwave Engineering\", 4th edition. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import skrf as rf\n",
    "\n",
    "rf.stylely()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Matching with Lumped Elements\n",
    "To begin, let's assume that the matching network is lossless and the feeding line characteristic impedance is $Z_0$:  \n",
    "\n",
    "<img src=\"figures/Impedance_matching_lumped1.svg\">\n",
    "\n",
    "The simplest type of matching network is the \"L\" network, which uses two reactive elements to match an arbitrary load impedance. Two possible configuration exist and are illustrated by the figures below. In either configurations, the reactive elements can be inductive of capacitive, depending on the load impedance.\n",
    "\n",
    "<img src=\"figures/Impedance_matching_lumped2.svg\">\n",
    "<img src=\"figures/Impedance_matching_lumped3.svg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's assume the load is $Z_L = 200 - 100j \\Omega$ for a line $Z_0=100\\Omega$ at the frequency of 500 MHz. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Z_L = 200 - 100j\n",
    "Z_0 = 100\n",
    "f_0_str = '500MHz'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's define the `Frequency` and load `Network`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# frequency band centered on the frequency of interest\n",
    "frequency = rf.Frequency(start=300, stop=700, npoints=401, unit='MHz')\n",
    "# transmission line Media\n",
    "line = rf.DefinedGammaZ0(frequency=frequency, z0=Z_0)\n",
    "# load Network\n",
    "load = line.load(rf.zl_2_Gamma0(Z_0, Z_L))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We are searching for a L-C Network corresponding to the first configuration above:\n",
    "<img src=\"figures/Impedance_matching_lumped4.svg\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def matching_network_LC_1(L, C):\n",
    "    ' L and C in nH and pF'\n",
    "    return line.inductor(L*1e-9)**line.shunt_capacitor(C*1e-12)**load\n",
    "\n",
    "def matching_network_LC_2(L, C):\n",
    "    ' L and C in nH and pF'\n",
    "    return line.capacitor(C*1e-12)**line.shunt_inductor(L*1e-9)**load"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finding the set of inductance $L$ and the capacitance $C$ which matches the load is an optimization problem. The `scipy` package provides the necessary optimization function(s) for that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.optimize import minimize\n",
    "\n",
    "# initial guess values\n",
    "L0 = 10 # nH\n",
    "C0 = 1 # pF\n",
    "x0 = (L0, C0)\n",
    "# bounds\n",
    "L_minmax = (1, 100) #nH\n",
    "C_minmax = (0.1, 10) # pF\n",
    "\n",
    "# the objective functions minimize the return loss at the target frequency f_0\n",
    "def optim_fun_1(x, f0=f_0_str):\n",
    "    _ntw = matching_network_LC_1(*x)\n",
    "    return np.abs(_ntw[f_0_str].s).ravel()\n",
    "\n",
    "def optim_fun_2(x, f0=f_0_str):\n",
    "    _ntw = matching_network_LC_2(*x)\n",
    "    return np.abs(_ntw[f_0_str].s).ravel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "res1 = minimize(optim_fun_1, x0, bounds=(L_minmax, C_minmax))\n",
    "print(f'Optimum found for LC network 1: L={res1.x[0]} nH and C={res1.x[1]} pF')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "res2 = minimize(optim_fun_2, x0, bounds=(L_minmax, C_minmax))\n",
    "print(f'Optimum found for LC network 2: L={res2.x[0]} nH and C={res2.x[1]} pF')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ntw1 = matching_network_LC_1(*res1.x)\n",
    "ntw2 = matching_network_LC_2(*res2.x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ntw1.plot_s_mag(lw=2, label='LC network 1')\n",
    "ntw2.plot_s_mag(lw=2, label='LC network 2')\n",
    "plt.ylim(bottom=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Single-Stub Matching\n",
    "Matching can be made with a piece of open-ended or shorted transmission line ( _stub_ ), connected either in parallel ( _shunt_ ) or in series. In the example below, a matching network is realized from a shorted transmission line of length ($\\theta_{stub}$) connected in parallel, in association with a series transmission line ($\\theta_{line}$). Let's assume a load impedance $Z_L=60 - 80j$ connected to a 50 Ohm transmission line. \n",
    "\n",
    "<img src=\"figures/Impedance_matching_stub1.svg\">\n",
    "\n",
    "Let's match this load at 2 GHz: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Z_L = 60 - 80j\n",
    "Z_0 = 50\n",
    "f_0_str = '2GHz'\n",
    "# Frequency, wavenumber and transmission line media\n",
    "freq = rf.Frequency(start=1, stop=3, npoints=301, unit='GHz')\n",
    "beta = freq.w/rf.c\n",
    "line = rf.DefinedGammaZ0(freq, gamma=1j*beta, z0=Z_0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def resulting_network(theta_delay, theta_stub):\n",
    "    \"\"\"\n",
    "    Return a loaded single stub matching network\n",
    "\n",
    "    NB: theta_delay and theta_stub lengths are in deg\n",
    "    \"\"\"\n",
    "    delay_load = line.delay_load(rf.zl_2_Gamma0(Z_0, Z_L), theta_delay)\n",
    "    shunted_stub = line.shunt_delay_short(theta_stub)\n",
    "    return shunted_stub ** delay_load"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Optimize the matching network variables `theta_delay` and `theta_stub` to match the resulting 1-port network ($|S|=0$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.optimize import minimize\n",
    "\n",
    "\n",
    "def optim_fun(x):\n",
    "    return resulting_network(*x)[f_0_str].s_mag.ravel()\n",
    "\n",
    "x0 = (50, 50)\n",
    "bnd = (0, 180)\n",
    "res = minimize(optim_fun, x0, bounds=(bnd, bnd))\n",
    "print(f'Optimum found for: theta_delay={res.x[0]:.1f} deg and theta_stub={res.x[1]:.1f} deg')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optimized network at f0\n",
    "ntw = resulting_network(*res.x)\n",
    "ntw.plot_s_db(lw=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
